namespace Sustainalytics.ESGComputing.Service.Mappers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;

    using Sustainalytics.Entities;
    using Sustainalytics.ESGRatings.BusinessLogic.Computing;
    using Sustainalytics.ESGRatings.DataContract.ESG;
    using Sustainalytics.ESGRatings.DataContract.Governance;
    using Sustainalytics.ESGRatings.Primitives;

    using CompanyRatingDto = Sustainalytics.ESGRatings.DataContract.Governance.CompanyRatingDto;
    using Serilog;

    public class GovernanceComputeMap
    {
        private readonly Dictionary<int, string> _issueIndexingDictionary = new Dictionary<int, string>();

        private readonly string _regex = @"[E|S|G|C]{1,2}";

        private readonly GovernanceComputeResult _result;

        public GovernanceComputeMap(GovernanceComputeResult result)
        {
            this._result = result;

            try
            {
                var capturedStrings = new List<string>();

                // _issueIndexingDictionary.Add(0, "CG.0");
                var index = 1;

                foreach (var themeMap in this._result.Configuration.ClustersMap)
                {
                    capturedStrings.Add(
                        Regex.Match(themeMap, this._regex, RegexOptions.IgnorePatternWhitespace).Groups[0].Captures[0]
                            .Value);
                    this._issueIndexingDictionary.Add(index, themeMap);
                    index++;
                }

                if (capturedStrings.Distinct().Count() != 1)
                {
                    throw new ArgumentException();
                }

                this._issueIndexingDictionary.Add(0, capturedStrings.Distinct().FirstOrDefault() + ".0");
            }
            catch (ArgumentException e)
            {
                Log.Error(e, "");
            }
        }

        public IEnumerable<CompanyRatingDto> GetRatings()
        {
            if (this._result.Ratings == null)
            {
                yield break;
            }

            var companiesCount = this._result.Ratings.CompaniesCount;
            var ratings = this._result.Ratings;

            var ax = this.ComputeCompaniesRatings(companiesCount, ratings);

            for (var i = 0; i < companiesCount; i++)
            {
                yield return ax[i];
            }
        }

        public IndicatorsGroupsDto ToIndicatorsGroupsDto()
        {
            try
            {
                var indicatorsGroups = new IndicatorsGroupsDto
                {
                    All =
                                                   new ClusterDto
                                                   {
                                                       Id = 0,
                                                       Name =
                                                               this
                                                               ._issueIndexingDictionary[0]
                                                   },
                    Themes = new List<ClusterDto>(),
                    Clusters = new List<ClusterDto>()
                };

                for (var index = 1; index < this._issueIndexingDictionary.Count; index++)
                {
                    indicatorsGroups.Clusters.Add(
                        new ClusterDto { Id = index, Name = this._issueIndexingDictionary[index] });
                }

                return indicatorsGroups;
            }
            catch (KeyNotFoundException keyNotFoundException)
            {
                Log.Error(keyNotFoundException, "");
            }

            return null;
        }

        private bool CompanyHasPeersInSelection(int ratingIndex)
        {
            return
                !(this._result.Ratings.GroupCounts[ratingIndex] == 1 && this._result.Ratings.Rankings[ratingIndex] == 1);
        }

        private Dictionary<int, CompanyRatingDto> ComputeCompaniesRatings(
            int companiesCount,
            GovernanceRatingsResult ratings)
        {
            var clustersCount = this._result.Ratings.IssuesCount;

            var ax = Enumerable.Range(0, companiesCount)
                .ToDictionary(
                    companyId => companyId,
                    companyId => new CompanyRatingDto { CompanyId = this.GetCompanyId(companyId) });

            for (var clusterIndex = 0; clusterIndex < clustersCount; clusterIndex++)
            {
                for (var companyIndex = 0; companyIndex < companiesCount; companyIndex++)
                {
                    var unorderedCompanyRating = ax[companyIndex];

                    var ratingIndex = (clusterIndex * companiesCount) + companyIndex;

                    unorderedCompanyRating.Scores.Add(this.GetIssueScore(ratingIndex, clusterIndex));

                    foreach (var comparativeGroupType in Enum.GetValues(typeof(PerformanceType)).Cast<PerformanceType>())
                    {
                        var rankingIndex = ratingIndex + ((int)comparativeGroupType * clustersCount * companiesCount);
                        var companyId = ratings.CompanyIndices[rankingIndex];

                        var orderedCompanyRating = ax[companyId];
                        if (!orderedCompanyRating.CompanyBenchmarks.ContainsKey(comparativeGroupType))
                        {
                            orderedCompanyRating.CompanyBenchmarks.Add(
                                comparativeGroupType,
                                new CompanyComparativeGroupBenchmarksDto());
                        }

                        this.ComputeCompanyRatings(
                            orderedCompanyRating,
                            rankingIndex,
                            clusterIndex,
                            comparativeGroupType);
                    }
                }
            }

            return ax;
        }

        private void ComputeCompanyRatings(
            CompanyRatingDto orderedCompanyRating,
            int ratingIndex,
            int clusterIndex,
            PerformanceType comparativeGroupType)
        {
            var issueBenchmark = this.GetIssueBenchmark(ratingIndex, clusterIndex);
            orderedCompanyRating.CompanyBenchmarks[comparativeGroupType].Benchmarks.Add(issueBenchmark);
        }

        private string GetClusterCode(int clusterIndex)
        {
            return this._issueIndexingDictionary[clusterIndex];
        }

        private Guid GetCompanyId(int index)
        {
            return this._result.CompaniesMap[index];
        }

        private IssueBenchmarksDto GetIssueBenchmark(int ratingIndex, int clusterIndex)
        {
            var companyGroupBenchmarks = new CompanyGroupValuesDto(
                this._result.Ratings.Rankings[ratingIndex],
                this._result.Ratings.Percentile[ratingIndex],
                this._result.Ratings.GroupCounts[ratingIndex]);

            return new IssueBenchmarksDto
            {
                Issue = this.GetClusterCode(clusterIndex),
                IssueBenchmark = companyGroupBenchmarks
            };
        }

        private IssueScoreDto GetIssueScore(int ratingIndex, int clusterIndex)
        {
            return new IssueScoreDto
            {
                Issue = this.GetClusterCode(clusterIndex),
                Score = this._result.Ratings.Ratings[ratingIndex],
                WeightedScore =
                               this._result.IncludeWeightedScores
                                   ? this._result.Ratings.IssuesWeightedScores[ratingIndex]
                                   : 0,
                Weight =
                               this._result.IncludeWeights ? this._result.Ratings.Weights[ratingIndex] : 0
            };
        }
    }
}